﻿using DynamicToolkit;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Xml;
using System.Collections.Generic;
using System.Collections;
using System.Dynamic;
using System.Linq.Expressions;
using System.Linq;
using System.Reflection;
using System.IO;
using DynamicToolkit.Tests.Mocks;

namespace DynamicToolkit.Tests
{    
    /// <summary>
    ///This is a test class for DynamicXpathTest and is intended
    ///to contain all DynamicXpathTest Unit Tests
    ///</summary>
    [TestClass()]
    public class DynamicXpathTest
    {

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion

        private static string TestXmlData
        {
            get
            {
                return Properties.Resources.Finance;
            }
        }

        private static XmlDocument TestDocument
        {
            get
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(TestXmlData);
                return doc;
            }
        }

        private static dynamic CreateDynamicXpath()
        {
            return DynamicXpath.Load(TestDocument);
        }

        /// <summary>
        ///A test for Attribute
        ///</summary>
        [TestMethod()]
        public void Attribute_ShouldReturnCorrectAttributeValue()
        {
            dynamic target = CreateDynamicXpath();
            string expected = "2010-05-09T15:00:00Z";
            string actual = target.Stocks.Attribute("date");
            Assert.AreEqual(expected, actual);
        }

        /// <summary>
        ///A test for Attributes
        ///</summary>
        [TestMethod()]
        public void Attributes_ShouldReturnCorrectNumberOfAttributes()
        {
            dynamic target = CreateDynamicXpath();
            string expectedValue = "2010-05-09T15:00:00Z";
            IEnumerable<string> actual = target.Stocks.Attributes("date");
            Assert.AreEqual(2, actual.Count());
            Assert.AreEqual(expectedValue, actual.First());
        }

        /// <summary>
        ///A test for GetDocument
        ///</summary>
        [TestMethod()]
        public void GetDocument_ShouldReturnUnderlyingXmlDocument()
        {
            dynamic target = CreateDynamicXpath();
            XmlDocument actual = target.GetDocument();
            Assert.IsNotNull(actual);
        }

        /// <summary>
        ///A test for GetRoot
        ///</summary>
        [TestMethod()]
        public void GetRoot_ShouldReturnNewDynamicXpathBasedAtDocumentRoot()
        {
            dynamic target = CreateDynamicXpath().Stocks;
            DynamicXpath actual = target.GetRoot();
            Assert.AreEqual("/Finance", actual.ToXpath());
        }

        /// <summary>
        ///A test for Load
        ///</summary>
        [TestMethod()]
        public void Load_ShouldCreateDyanamicXpathFromGivenFile()
        {
            string filename = Path.GetTempFileName();

            try
            {
                File.WriteAllText(filename, TestXmlData);
                dynamic actual = DynamicXpath.Load(filename);
                Assert.AreEqual("/Finance", actual.ToXpath());
            }
            finally
            {
                File.Delete(filename);
            }
        }

        /// <summary>
        ///A test for Load
        ///</summary>
        [TestMethod()]
        public void Load_LoadingDynamicXpathWithXmlElementShouldGenerateCorrectInitialXpath()
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(TestXmlData);

            XmlElement stocksElement = doc.SelectSingleNode("/Finance/Stocks") as XmlElement;
            dynamic target = DynamicXpath.Load(stocksElement);

            const string expected = "/Finance/Stocks";
            Assert.AreEqual(expected, target.ToXpath());
        }

        /// <summary>
        ///A test for Load
        ///</summary>
        [TestMethod()]
        public void Load_LoadingDynamicXpathWithXmlDocumentShouldGenerateCorrectInitialXpath()
        {
            XmlDocument doc = TestDocument;
            dynamic target = DynamicXpath.Load(doc);

            const string expected = "/Finance";
            Assert.AreEqual(expected, target.ToXpath());
        }

        /// <summary>
        ///A test for LoadXml
        ///</summary>
        [TestMethod()]
        public void LoadXml_ShouldCreateDyanamicXpathFromGivenXmlData()
        {
            dynamic actual = DynamicXpath.LoadXml(TestXmlData);
            Assert.AreEqual("/Finance", actual.ToXpath());
        }

        /// <summary>
        ///A test for Node
        ///</summary>
        [TestMethod()]
        public void Node_ShouldReturnApproriateNode()
        {
            dynamic target = CreateDynamicXpath();
            XmlNode actual = target.Stocks.Node();
            Assert.AreEqual("Stocks", actual.Name);
        }

        /// <summary>
        ///A test for Nodes
        ///</summary>
        [TestMethod()]
        public void Nodes_ShouldReturnCorrectNumberOfNodes()
        {
            dynamic target = CreateDynamicXpath();
            XmlNodeList actual = target.Stocks.Nodes();
            Assert.AreEqual(2, actual.Count);
            Assert.IsTrue(actual.Cast<XmlNode>().All(n => n.Name == "Stocks"));
        }

        /// <summary>
        ///A test for GetEnumerator
        ///</summary>
        [TestMethod()]
        public void GetEnumerator_ShouldBeAbleToEnumerateDynamicXpathUsingStronglyTypedEnumerator()
        {
            dynamic target = CreateDynamicXpath();
            IEnumerator<dynamic> actual = target.Stocks.Stock.GetEnumerator();

            int actualCount = 0;

            while (actual.MoveNext())
            {
                actualCount++;
            }

            Assert.AreEqual(8, actualCount);
        }

        /// <summary>
        ///A test for System.Collections.IEnumerable.GetEnumerator
        ///</summary>
        [TestMethod()]
        public void GetEnumerator_ShouldBeAbleToEnumerateUsingWeaklyTypedEnumerator()
        {
            dynamic target = CreateDynamicXpath();
            IEnumerator actual = (target.Stocks.Stock as IEnumerable).GetEnumerator();

            int actualCount = 0;

            while (actual.MoveNext())
            {
                actualCount++;
            }

            Assert.AreEqual(8, actualCount);
        }

        /// <summary>
        ///A test for ToXpath
        ///</summary>
        [TestMethod()]
        public void ToXpath_ShouldGenerateCorrectXpathForRootElement()
        {
            dynamic target = CreateDynamicXpath();
            string actual = target.ToXpath();
            Assert.AreEqual("/Finance", actual);
        }

        /// <summary>
        ///A test for Value
        ///</summary>
        [TestMethod()]
        public void Value_ShouldReturnCorrectNodeValue()
        {
            dynamic target = CreateDynamicXpath();
            string actual = target.Stocks.Stock.Symbol.Value();
            Assert.AreEqual("GOOG", actual);
        }

        /// <summary>
        ///A test for Values
        ///</summary>
        [TestMethod()]
        public void Values_ShouldReturnCorrectNodeValues()
        {
            dynamic target = CreateDynamicXpath();
            IEnumerable<string> actual = target.Stocks.Stock.Symbol.Values();
            Assert.AreEqual(8, actual.Count());
            Assert.AreEqual("GOOG", actual.First());
        }

        /// <summary>
        ///A test for Item
        ///</summary>
        [TestMethod()]
        public void Item_ExpressionPredicateShouldTranslateToXpathPredicate()
        {
            dynamic target = CreateDynamicXpath();
            IMockStock stock = null;
            Expression<Func<dynamic, bool>> predicate = (dynamicXpath => stock.Symbol == "AAPL");
            dynamic actual = target.Stocks.Stock[predicate];
            Assert.AreEqual("/Finance/Stocks/Stock[Symbol = 'AAPL']", actual.ToXpath());
        }

        /// <summary>
        ///A test for Item
        ///</summary>
        [TestMethod()]
        public void Item_ComplexExpressionPredicateShouldTranslateToXpathPredicate()
        {
            dynamic target = CreateDynamicXpath();
            Expression<Func<IMockStock, bool>> predicate = (s => s.Symbol == "AAPL" || s.Price > 300);
            dynamic actual = target.Stocks.Stock[predicate].Symbol;
            Assert.AreEqual("/Finance/Stocks/Stock[Symbol = 'AAPL' or Price > 300]/Symbol", actual.ToXpath());
            Assert.AreEqual(4, actual.Count());

            List<string> actualList = actual;
            Assert.IsTrue(actualList.All(symbol => symbol == "AAPL" || symbol == "GOOG"));
        }

        /// <summary>
        ///A test for Item
        ///</summary>
        [TestMethod()]
        public void Item_ShouldIncorporateStringPredicateIntoGeneratedXpath()
        {
            dynamic target = CreateDynamicXpath();
            dynamic actual = target.Stocks.Stock["Symbol = 'AAPL'"];
            Assert.AreEqual("/Finance/Stocks/Stock[Symbol = 'AAPL']", actual.ToXpath());
        }

        /// <summary>
        ///A test for Item
        ///</summary>
        [TestMethod()]
        public void Item_DisjunctionExampleExpressionShouldTranslateToXpathPredicate()
        {
            dynamic target = CreateDynamicXpath();
            dynamic actual = target.Stocks.Stock[new { Symbol = "AAPL" }, new { Price = 27 }].Symbol;
            Assert.AreEqual("/Finance/Stocks/Stock[(Symbol = 'AAPL') or (Price = 27)]/Symbol", actual.ToXpath());
            Assert.AreEqual(3, actual.Count());

            List<string> actualList = actual;
            Assert.IsTrue(actualList.All(symbol => symbol == "AAPL" || symbol == "MSFT"));
        }

        /// <summary>
        ///A test for Item
        ///</summary>
        [TestMethod()]
        public void Item_ElementExampleExpressionShouldTranslateToXpathPredicate()
        {
            dynamic target = CreateDynamicXpath();
            dynamic actual = target.Stocks.Stock[new { Symbol = "AAPL", Price = 270 }].Symbol;
            Assert.AreEqual("/Finance/Stocks/Stock[Symbol = 'AAPL' and Price = 270]/Symbol", actual.ToXpath());
            Assert.AreEqual(1, actual.Count());

            List<string> actualList = actual;
            Assert.IsTrue(actualList.All(symbol => symbol == "AAPL"));
        }

        /// <summary>
        ///A test for Item
        ///</summary>
        [TestMethod()]
        public void Item_AttributeExamplePredicateShouldTranslateToXpathPredicate()
        {
            dynamic target = CreateDynamicXpath();
            dynamic actual = target.Stocks[new AttributeExample(new { date = new DateTime(2010, 5, 9, 15, 0, 0, DateTimeKind.Utc) })];
            Assert.AreEqual("/Finance/Stocks[@date = '2010-05-09T15:00:00Z']", actual.ToXpath());
            Assert.AreEqual(1, actual.Count());
        }

        /// <summary>
        ///A test for Item
        ///</summary>
        [TestMethod()]
        public void Item_ChildIndexSelectorShouldSelectCorrectChildNode()
        {
            dynamic target = CreateDynamicXpath();
            dynamic actual = target.Stocks.Stock[1].Symbol;
            Assert.AreEqual("MSFT", actual.Value());
        }

        /// <summary>
        ///A test for op_Implicit
        ///</summary>
        [TestMethod()]
        public void op_Implicit_ImplicitConversionToStringShouldResultInValueOfTheFirstMatchingNode()
        {
            dynamic target = CreateDynamicXpath();
            string actual = target.Stocks.Stock.Symbol;
            Assert.AreEqual("GOOG", actual);
        }

        /// <summary>
        ///A test for op_Implicit
        ///</summary>
        [TestMethod()]
        public void op_Implicit_ImplicitConversionToStringEnumerableShouldResultInEnumerableContainingAllMatchingNodeValues()
        {
            dynamic target = CreateDynamicXpath();
            List<string> actual = target.Stocks.Stock.Symbol;
            Assert.AreEqual(8, actual.Count);
            Assert.AreEqual("GOOG", actual.First());
        }

        /// <summary>
        ///A test for op_Implicit
        ///</summary>
        [TestMethod()]
        public void op_Implicit_ImplicitConversionToXmlNodeShouldResultInTheFirstMatchingNode()
        {
            dynamic target = CreateDynamicXpath();
            XmlNode actual = target.Stocks.Stock.Symbol;
            Assert.AreEqual("GOOG", actual.InnerText);
        }

        /// <summary>
        ///A test for op_Implicit
        ///</summary>
        [TestMethod()]
        public void op_Implicit_ImplicitConversionToXmlNodeListShouldResultAllMatchingNodes()
        {
            dynamic target = CreateDynamicXpath();
            XmlNodeList actual = target.Stocks.Stock.Symbol;
            Assert.AreEqual(8, actual.Count);
            Assert.AreEqual("GOOG", actual[0].InnerText);
        }

        [TestMethod()]
        public void Value_ShouldReturnCorrectNodeValueWithSpecifiedType()
        {
            dynamic target = CreateDynamicXpath();
            decimal actual = target.Stocks.Stock.Price.Value<decimal>();
            Assert.AreEqual(670.11M, actual);
        }
        
        [TestMethod()]
        public void Values_ShouldReturnCorrectNodeValuesWithSpecifiedType()
        {
            dynamic target = CreateDynamicXpath();
            IEnumerable<double> actual = target.Stocks.Stock.Price.Values<double>();
            Assert.AreEqual(8, actual.Count());
            Assert.AreEqual(670.11D, actual.First());
        }
    }
}
